package com.bestcem.xm.user.handler.excel.read.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.util.ConverterUtils;
import com.bestcem.xm.common.core.constant.RegTypes;
import com.bestcem.xm.user.constant.BusinessConstant;
import com.bestcem.xm.user.constant.UserGroupConstant;
import com.bestcem.xm.user.enums.GroupImportDataErrorEnum;
import com.bestcem.xm.user.enums.GroupImportErrorEnum;
import com.bestcem.xm.user.util.business.GroupLevelUtil;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;

import java.text.MessageFormat;
import java.util.*;
import java.util.regex.Pattern;

/**
 * 组织导出首页sheet监听器
 *
 * @author wangyunyun <yunyun.wang@idiaoyan.com>
 * @version v1.0
 * @date 2022/2/8 17:47
 */
@Getter
@Setter
@NoArgsConstructor
@Slf4j
public class GroupIndexReadListener extends AnalysisEventListener<Map<Integer, String>> {

    private Map<Integer, String> headers;

    private List<List<String>> dataList = new ArrayList<>();

    private Integer errorStatus;

    private String errorMsg;

    /**
     * 数据格式是否有异常: true:有; false:无
     */
    private boolean hasDataError = false;

    private boolean hasNext = true;

    private int totalGroupCount = NumberUtils.INTEGER_ZERO;

    private String sheetName;

    /**
     * 根组织集合: 只有一个 超出一个则为错误数据
     */
    private Set<String> rootGroupNames = new HashSet<>();

    /**
     * 组织名称路径集合: 校验是否有重复行
     */
    private Set<String> fullGroupNames = new HashSet<>();

    /**
     * 这里会一行行的返回头
     *
     * @param headMap 表头
     * @param context 表格context
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:27
     */
    @Override
    public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
        // 获取sheet名称
        String sheetNameByContext = context.readSheetHolder().getSheetName();

        // 设置sheet名称
        this.sheetName = StringUtils.isNotBlank(this.sheetName) ? this.sheetName : sheetNameByContext;

        //  转换成 Map<Integer,String>
        this.headers = ConverterUtils.convertToStringMap(headMap, context);

        // 校验表头格式
        checkHeaders(sheetNameByContext);
    }

    /**
     * 每读到一行数据都调用invoke方法
     *
     * @param data    key为列号，value为单元格的内容
     * @param context 表格context
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 13:22
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        // 设置行数据
        List<String> row = new ArrayList<>(data.values());

        // 过滤空行
        boolean isBlankRow = true;
        for (String cloumnValue : row) {
            if (StringUtils.isNotBlank(cloumnValue)) {
                isBlankRow = false;
                break;
            }
        }
        if (isBlankRow) {
            return;
        }

        // 校验行数据
        checkRows(row);

        // 把数据放到dataList里面，便于统一处理
        this.dataList.add(row);
    }

    /**
     * 读完sheet的处理
     *
     * @param context 表格context
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:28
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 读完sheet所有数据，做统一处理
        doAfterAllAnalysedCustom();
    }

    /**
     * 是否需要继续读表格下一行数据
     *
     * @param context 表格context
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:28
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        return this.hasNext && this.errorStatus == null;
    }

    /**
     * sheet格式是否有错误
     *
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:29
     */
    public boolean hasReadError() {
        return errorStatus != null;
    }


    /**
     * sheet数据格式是否有错误
     *
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:30
     */
    public boolean hasDataError() {
        return hasDataError;
    }

    /**
     * 设置层级名称集合
     *
     * @return Map<String>
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:30
     */
    public Map<Integer, String> getLevelNames() {
        Map<Integer, String> levelNames = new HashMap<>();
        for (Map.Entry<Integer, String> entry : headers.entrySet()) {
            Integer headerNo = entry.getKey();
            levelNames.put(headerNo, StringUtils.chop(headers.get(headerNo)));
        }

        return levelNames;
    }

    /**
     * 校验表头是否正确
     *
     * @param sheetNameByContext sheet名称
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:30
     */
    private boolean checkHeaders(String sheetNameByContext) {
        // 校验sheet名称
        if (!sheetNameByContext.equals(this.sheetName)) {
            return doWholeError(GroupImportErrorEnum.SHEET_FORMAT_ERROR, this.sheetName);
        }

        // 校验层级数是否符合要求的范围
        if (BusinessConstant.USER_LEVEL_MIN_COUNT > this.headers.size() || BusinessConstant.USER_LEVEL_MAX_COUNT < this.headers.size()) {
            return doWholeError(GroupImportErrorEnum.HEAD_DATA_COUNT_ERROR,
                    BusinessConstant.USER_LEVEL_MIN_COUNT, BusinessConstant.USER_GROUP_MAX_COUNT);
        }

        // 校验表头
        Set<String> levelNameSet = new HashSet<>();
        for (Map.Entry<Integer, String> entry : this.headers.entrySet()) {
            Integer key = entry.getKey();
            String levelName = this.headers.get(key);
            // 过滤错误信息列, 如果存在则报错
            Integer lastHeaderKey = this.headers.size() - 1;
            if (key.equals(lastHeaderKey) && UserGroupConstant.GROUP_INDEX_SHEET_ERROR_ROW_TITLE.equals(this.headers.get(lastHeaderKey))) {
                return doWholeError(GroupImportErrorEnum.HEAD_BLANK_ERROR, this.sheetName);
            }

            // 校验表头之间是否有空列
            if (StringUtils.isBlank(levelName)) {
                return doWholeError(GroupImportErrorEnum.HEAD_BLANK_ERROR, this.sheetName);
            }
            // 处理层级名称的*必填问题
            if (levelName.endsWith("*")) {
                levelName = levelName.substring(0, levelName.length() - 1);
            }

            // 校验层级名称长度
            if (levelName.length() > BusinessConstant.USER_LEVEL_NAME_MAX_LENGTH) {
                return doWholeError(GroupImportErrorEnum.LEVEL_NAME_LENGTH_ERROR, BusinessConstant.USER_LEVEL_NAME_MAX_LENGTH);
            }

            // 校验层级名称格式
            if (GroupLevelUtil.checkLevelNameInvalid(levelName)) {
                return doWholeError(GroupImportErrorEnum.LEVEL_NAME_FORMAT_ERROR);
            }

            // 校验层级名称是否重复
            if (levelNameSet.contains(levelName)) {
                return doWholeError(GroupImportErrorEnum.LEVEL_NAME_REPECT_ERROR);
            } else {
                levelNameSet.add(levelName);
            }
        }

        return true;
    }

    /**
     * 校验行数据是否正确
     *
     * @param row 行数据
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private boolean checkRows(List<String> row) {
        // 校验当前sheet的组织数量
        this.totalGroupCount++;
        if (BusinessConstant.USER_GROUP_MAX_COUNT < this.totalGroupCount) {
            return doWholeError(GroupImportErrorEnum.GROUP_MAX_ERROR, BusinessConstant.USER_GROUP_MAX_COUNT_STR);
        }

        // 校验: 根组织名称不能为空
        if (StringUtils.isBlank(row.get(0))) {
            return doWholeError(GroupImportErrorEnum.GROUP_ROOT_BLANK_ERROR);
        }

        // 校验: 根组织有且只有一个
        this.rootGroupNames.add(row.get(0));
        if (this.rootGroupNames.size() > 1) {
            return doWholeError(GroupImportErrorEnum.GROUP_ROOT_COUNT_ERROR);
        }

        // 校验行数据是否正确, 写error文件
        if (!this.hasReadError()) {
            checkRowByData(row);
        }

        return true;
    }

    /**
     * 校验行数据是否正确, 写error文件
     *
     * @param row 行数据
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private void checkRowByData(List<String> row) {
        Set<String> errorMsgs = new HashSet<>();

        // 校验是否有重复行
        String fullGroupName = StringUtils.join(row, ",");
        if (fullGroupNames.contains(fullGroupName)) {
            doDataError(errorMsgs, GroupImportDataErrorEnum.GROUP_REPECT_ERROR);
        } else {

            fullGroupNames.add(fullGroupName);
            // 校验行数据格式
            for (String groupName : row) {
                // 校验组织名称
                if (StringUtils.isNotBlank(groupName)) {
                    // 校验组织名称长度
                    if (groupName.length() > BusinessConstant.USER_GROUP_NAME_MAX_LENGTH) {
                        doDataError(errorMsgs, GroupImportDataErrorEnum.GROUP_NAME_LENGTH_ERROR, BusinessConstant.USER_GROUP_NAME_MAX_LENGTH);
                        continue;
                    }
                    // 校验组织名称格式
                    Pattern pattern = Pattern.compile(RegTypes.GROUP_NAME);
                    if (!pattern.matcher(groupName).matches()) {
                        doDataError(errorMsgs, GroupImportDataErrorEnum.GROUP_NAME_FORMAT_ERROR);
                    }
                }
            }
        }

        if (CollectionUtils.isNotEmpty(errorMsgs)) {
            row.add(StringUtils.join(errorMsgs, ";"));
        }
    }

    /**
     * 处理错误信息: sheet数据格式错误
     *
     * @param errorMsgs 错误信息
     * @param errorEnum 错误枚举
     * @param arguments 错误信息参数数组
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private void doDataError(Set<String> errorMsgs, GroupImportDataErrorEnum errorEnum, Object... arguments) {
        errorMsgs.add(MessageFormat.format(errorEnum.getMsg(), arguments));
        this.hasDataError = true;
    }

    /**
     * 处理错误信息: excel文件格式错误
     *
     * @param errorEnum 错误枚举
     * @param arguments 错误信息参数数组
     * @return boolean
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private boolean doWholeError(GroupImportErrorEnum errorEnum, Object... arguments) {
        this.errorStatus = errorEnum.getStatus();
        this.errorMsg = MessageFormat.format(errorEnum.getMsg(), arguments);

        return false;
    }

    /**
     * 读完sheet所有数据，做统一处理
     *
     * @return
     * @author wangyunyun <yunyun.wang@idiaoyan.com>
     * @date 2022/3/31 14:31
     */
    private boolean doAfterAllAnalysedCustom() {
        // 校验: 根组织有且只有一个
        if (CollectionUtils.isEmpty(this.dataList)) {
            return doWholeError(GroupImportErrorEnum.GROUP_ROOT_COUNT_ERROR);
        }

        return true;
    }

}
